package com.gitee.wsl.mathematics.geometry.d2.circle

import com.gitee.wsl.io.dataproxy.SingeProxy
import com.gitee.wsl.mathematics.coordinate.polar.ext.polar
import com.gitee.wsl.mathematics.geometry.d2.AbstractCloseShape
import com.gitee.wsl.mathematics.geometry.d2.contour.ShapeContour
import com.gitee.wsl.mathematics.geometry.d2.contour.contour
import com.gitee.wsl.mathematics.geometry.d2.triangle.Triangle
import com.gitee.wsl.mathematics.vector.ext.between
import com.gitee.wsl.mathematics.vector.vec2.Vec2f
import com.gitee.wsl.unit.angle.AngleUnit
import kotlin.math.abs
import kotlin.math.sqrt

open class Circle(a: Vec2f, override var radius :Float): AbstractCloseShape(proxy = SingeProxy(a)),
    CircleShape2d<Float, Vec2f> {
  constructor(x: Number, y: Number, radius: Number):this(Vec2f(x, y),radius.toFloat())
  constructor(a: Vec2f,b:Vec2f):this((a + b) * 0.5,(b-((a + b) * 0.5)).length)
  constructor(triangle: Triangle):this(triangle.center,(triangle.a - triangle.center).length)
    //override fun getBounds() = Rectangle(center.x - radius, center.y - radius, center.x + radius, center.y + radius)

    override fun createPoint2(x: Float, y: Float): Vec2f = Vec2f(x,y)

    override fun createSegment(
        startAngle: AngleUnit,
        sweepAngle: AngleUnit,
    ) = CircleSegment(this,startAngle, sweepAngle)

    override fun create(p: Vec2f, radius: Float): CircleShape2d<Float, Vec2f> = Circle(p,radius)

    fun projectedPoint(p: Vec2f) = polar(center, AngleUnit.between(center, p), radius)

    override fun getPointOnAngle(angle: AngleUnit) = polar(center, angle, radius)

    companion object{
        val INVALID = Circle(Vec2f.positiveInfinity, 0.0f)
        /**
         * Constructs a [Circle] where the perimeter passes through the three points.
         */
        fun fromPoints(a: Vec2f, b: Vec2f, c: Vec2f): Circle {
            val det = (a.x - b.x) * (b.y - c.y) - (b.x - c.x) * (a.y - b.y)

            if (abs(det) < 1E-7) {
                return INVALID
            }

            val offset = b.x * b.x + b.y * b.y
            val bc = (a.x * a.x + a.y * a.y - offset) / 2
            val cd = (offset - c.x * c.x - c.y * c.y) / 2
            val x = (bc * (b.y - c.y) - cd * (a.y - b.y)) / det
            val y = (cd * (a.x - b.x) - bc * (b.x - c.x)) / det
            val radius = sqrt(
                (b.x - x) * (b.x - x) + (b.y - y) * (b.y - y)
            )

            return Circle(x, y, radius)
        }
    }

    /** Returns [ShapeContour] representation of the [Circle]. */
    override val contour: ShapeContour
        get() {
            if(this == INVALID) {
                return ShapeContour.EMPTY
            }
            val x = center.x - radius
            val y = center.y - radius
            val width = radius * 2.0
            val height = radius * 2.0
            val kappa = 0.5522848
            val ox = width / 2 * kappa        // control point offset horizontal
            val oy = height / 2 * kappa        // control point offset vertical
            val xe = x + width        // x-end
            val ye = y + height        // y-end
            val xm = x + width / 2        // x-middle
            val ym = y + height / 2       // y-middle

            return contour {
                moveTo(Vec2f(x, ym))
                curveTo(Vec2f(x, ym - oy), Vec2f(xm - ox, y), Vec2f(xm, y))
                curveTo(Vec2f(xm + ox, y), Vec2f(xe, ym - oy), Vec2f(xe, ym))
                curveTo(Vec2f(xe, ym + oy), Vec2f(xm + ox, ye), Vec2f(xm, ye))
                curveTo(Vec2f(xm - ox, ye), Vec2f(x, ym + oy), Vec2f(x, ym))
                close()
            }
        }


}